﻿using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Globalization;

using SharpGL.Media3D;
using SharpGL.SceneGraph;
using SharpGL.SceneGraph.Primitives;
using SharpGL.SceneGraph.Primitives.Text;
using SharpGL.SceneGraph.Assets;
using SharpGL.SceneGraph.Effects;
using SharpGL.Enumerations;

namespace SharpGL.Serialization
{
    public partial class SceneFileFormat : IFileFormat
    {
        private abstract class ScnnFileRecord
        {
            protected struct Caption
            {
                public Caption(string text, int index)
                {
                    this.text = text;
                    this.pointIndex = index;
                }
                private string text;
                private int pointIndex;

                public string Text
                {
                    get { return text; }
                    set { text = value; }
                }
                public int Index
                {
                    get { return pointIndex; }
                    set { pointIndex = value; }
                }

            }
            private List<Point3D> vertices = new List<Point3D>();
            private string normalizeLine(string arg)
            {
                while (arg.IndexOf("  ") != -1) arg = arg.Replace("  ", " ");
                return arg;
            }
            protected string[] arg;
            public CultureInfo c = new CultureInfo("ru");
            public Point3D ParseVertex(string X, string Y, string Z)
            {
                return new Point3D(float.Parse(X ?? "0.0", c),
                                  float.Parse(Y ?? "0.0", c),
                                  float.Parse(Z ?? "0.0", c));
            }
            public GLColor ParseColor(string R, string G, string B)
            {
                return new GLColor(float.Parse(R ?? "0.0", c) / 255,
                                   float.Parse(G ?? "0.0", c) / 255,
                                   float.Parse(B ?? "0.0", c) / 255, 1.0f);
            }
            public virtual void ParseLine(string line, string NumberDecimalSeparator = ".")
            {
                c.NumberFormat.NumberDecimalSeparator = NumberDecimalSeparator;
                line = normalizeLine(line);
                arg = line.Split(new char[] { ' ' });
            }
            public abstract SceneElement SceneElement { get; }
            protected List<Point3D> Vertices
            {
                get { return vertices; }
                set { vertices = value; }
            }
        }

        private class LINE : ScnnFileRecord
        {
            private List<Caption> Caption3D = new List<Caption>();
            private List<Caption> Caption2D = new List<Caption>();
            private GLColor color = new GLColor(0.5f, 0.5f, 0.5f, 1.0f);
            private float width = 1.0f;

            public override void ParseLine(string line, string NumberDecimalSeparator = ".")
            {
                base.ParseLine(line);
                switch (arg.ElementAt(0))
                {
                    case "*":
                        Vertices.Add(ParseVertex(arg[1], arg[2], arg[3]));
                        if (arg.Count() > 4)
                            switch (arg.ElementAt(4))
                            {
                                case "TEXT3D":
                                    if (arg.ElementAt(5).StartsWith("$"))
                                    {
                                        Caption3D.Add(new Caption(line.Substring(line.IndexOf("$") + 1), Vertices.Count - 1));
                                    }
                                    break;
                                case "TEXT2D":
                                        Caption2D.Add(new Caption(line.Substring(line.IndexOf("$") + 1), Vertices.Count - 1));
                                    break;
                            }
                        break;
                    case "#":
                        switch (arg.ElementAt(1))
                        {
                            case "LINEWIDTH":
                                width = float.Parse(arg.ElementAt(2), c);
                                break;
                            case "COLOR":
                                color = ParseColor(arg[2], arg[3], arg[4]);
                                break;
                            
                            default: break;
                        }
                        break;

                    default: break;
                }
            }

            public override SceneElement SceneElement
            {
                get
                {
                    var lineEffect = new OpenGLAttributesEffect();
                    lineEffect.AttributesGroup.EnableAttributes.EnableColorMaterial = true;
                    lineEffect.AttributesGroup.LineAttributes.Width = width;
                    lineEffect.AttributesGroup.CurrentAttributes.CurrentColor = color;
                    var line = new SolidPolyline() 
                    {   
                        Vertices = this.Vertices, 
                        Slices = 18 
                    };
                    line.Effects.Add(lineEffect);
                    foreach (Caption Caption in Caption3D)                    
                    {
                        var text3deffect = new OpenGLAttributesEffect();
                        text3deffect.AttributesGroup.EnableAttributes.EnableColorMaterial = true;
                        text3deffect.AttributesGroup.CurrentAttributes.CurrentColor = color;                        
                        var text3d = new Text3DObject()
                        {
                            Text = Caption.Text,
                            Position = Vertices[Caption.Index],
                            Direction = (Caption.Index == 0 ? (Vertices[1] - Vertices[0]) : (Vertices[Caption.Index] - Vertices[Caption.Index - 1])),
                            Font = new System.Drawing.Font("Arial", width * 7),
                            Angle = 90.0f,
                            Extrusion = 1.0f
                        };
                        text3d.Effects.Add(text3deffect);
                        line.Children.Add(text3d);
                    }
                    foreach (Caption Caption in Caption2D)     
                    {
                        var text2d = new Text2DObject()
                        {
                            Text = Caption.Text,
                            Position = Vertices[Caption.Index],
                            HorizontalAlignment = SceneGraph.Primitives.Text.HorizontalAlignment.Center,
                            VerticalAlignment = SceneGraph.Primitives.Text.VerticalAlignment.Center,
                            Font = new System.Drawing.Font("Arial", 10.0f, System.Drawing.FontStyle.Bold),
                        };
                        var text2DEffect = new OpenGLAttributesEffect();
                        text2DEffect.AttributesGroup.CurrentAttributes.CurrentColor = new GLColor(0.0f, 0.0f, 0.0f, 1.0f);
                        text2d.Effects.Add(text2DEffect);
                        line.Children.Add(text2d);
                    }
                    return line;
                }
            }
        }

        private class ZONE : ScnnFileRecord
        {
            private GLColor color = new GLColor(0.5f, 0.5f, 0.5f, 1.0f);

            public override void ParseLine(string line, string NumberDecimalSeparator = ".")
            {
                base.ParseLine(line);
                switch (arg.ElementAt(0))
                {
                    case "*":
                        Vertices.Add(ParseVertex(arg[1], arg[2], arg[3]));
                        break;
                    case "#":
                        switch (arg.ElementAt(1))
                        {
                            case "BGCOLOR":
                                color = ParseColor(arg[2], arg[3], arg[4]);
                                break;
                            default: break;
                        }
                        break;
                    default: break;
                }
            }
            public override SceneElement SceneElement
            {
                get 
                {
                    var polygon = new Polygon();
                    var polygonEffect = new OpenGLAttributesEffect();
                    polygon.AddFaceFromVertexData(Vertices.ToArray());
                    polygonEffect.AttributesGroup.EnableAttributes.EnableColorMaterial = true;
                    polygonEffect.AttributesGroup.CurrentAttributes.CurrentColor = color;
                    polygon.Effects.Add(polygonEffect);
                    return polygon;
                }
            }
        }


        private class RASTR : ScnnFileRecord
        {
            private List<Caption> Caption2D = new List<Caption>();
            public string path;

            public override void ParseLine(string line, string NumberDecimalSeparator = ".")
            {
                base.ParseLine(line, NumberDecimalSeparator);
                switch (arg.ElementAt(0))
                {
                    case "*":
                        Vertices.Add(new Point3D(float.Parse(arg.ElementAt(1) ?? "0.0", c),
                                                float.Parse(arg.ElementAt(2) ?? "0.0", c),
                                                float.Parse(arg.ElementAt(3) ?? "0.0", c)));

                        if (arg.Count() > 4)
                            switch (arg.ElementAt(4))
                            {
                                case "TEXT2D":
                                    if (arg.ElementAt(5).StartsWith("$"))
                                    {
                                        Caption2D.Add(new Caption(line.Substring(line.IndexOf("$") + 1), Vertices.Count - 1));
                                    }
                                    break;
                            }
                        break;
                    case "$":
                        path = (Path.GetDirectoryName(path) + @"\" + arg.ElementAt(1));
                        break;
                    default: break;
                }                                   
            }

            public override SceneElement SceneElement
            {
                get 
                {
                    var texquad = new TexturedQuad();
                    var texquadEffect = new OpenGLAttributesEffect();                    
                    texquad.A = Vertices[0];
                    texquad.B = Vertices[1];
                    texquad.C = Vertices[2];
                    texquad.D = Vertices[3];
                    texquad.CellSize = new System.Drawing.Size(256, 256);
                    texquad.Material = new Material()
                    {
                        Texture = new Texture2D()
                        {
                            Image = new System.Drawing.Bitmap(path)
                        }
                    };

                    texquadEffect.AttributesGroup.EnableAttributes.EnableTexture2D = true;
                    texquadEffect.AttributesGroup.EnableAttributes.EnableColorMaterial = true;
                    texquadEffect.AttributesGroup.ColorBufferAttributes.BlendingSourceFactor = BlendingSourceFactor.SourceAlpha;
                    texquadEffect.AttributesGroup.ColorBufferAttributes.BlendingDestinationFactor = BlendingDestinationFactor.OneMinusSourceAlpha;
                    texquadEffect.AttributesGroup.ColorBufferAttributes.EnableBlend = true;
                    //texquadEffect.AttributesGroup.LightingAttributes.TwoSided = true;
                    texquadEffect.AttributesGroup.TextureAttributes.TextureTarget = TextureTarget.Texture2D;
                    texquadEffect.AttributesGroup.TextureAttributes.TextureMinFilter = TextureMinFilter.Linear;
                    texquadEffect.AttributesGroup.TextureAttributes.TextureMagFilter = TextureMagFilter.Linear;
                    texquadEffect.AttributesGroup.CurrentAttributes.CurrentColor = new GLColor(1.0f, 1.0f, 1.0f, 0.7f);
                    texquad.Effects.Add(texquadEffect);

                    foreach (Caption Caption in Caption2D)
                    {
                        var text2d = new Text2DObject()
                        {
                            Text = Caption.Text,
                            Position = Vertices[Caption.Index],
                            HorizontalAlignment = SceneGraph.Primitives.Text.HorizontalAlignment.Center,
                            VerticalAlignment = SceneGraph.Primitives.Text.VerticalAlignment.Center,
                            Font = new System.Drawing.Font("Arial", 10.0f, System.Drawing.FontStyle.Bold),
                        };
                        var text2DEffect = new OpenGLAttributesEffect();
                        text2DEffect.AttributesGroup.CurrentAttributes.CurrentColor = new GLColor(0.0f, 0.0f, 0.0f, 1.0f);
                        text2d.Effects.Add(text2DEffect);
                        texquad.Children.Add(text2d);
                    }

                    return texquad;
                }
            }
        }

    }
}
